package com.gosophia.commons.persistence;

import java.lang.reflect.Method;

import javax.persistence.Id;

import org.apache.log4j.Logger;

/**
 * 抽象的实体基类。
 * 
 * 封装了实体主键属性 <code>id</code> 及其默认符合JPA规范的ORM映射配置，使用AUTO策略生成主键值。 此实体基类重写了
 * <code>equals()</code>方法，使用业务主键对实体对象进行比较； 另外也重写了 <code>hashCode()</code>
 * 方法，计算基于实体业务主键的哈希散列值。 所有继承自此抽象类的实体类都可使用 <code>@BusinessKey</code> 标识其业务主键。
 * 
 * @see BusinessKey
 * @author zhangtong
 * @since 2010-05-26
 * 
 */
public abstract class AbstractEntity {

	private static Logger log = Logger.getLogger(AbstractEntity.class);

//	public abstract Long getId();
	
	/**
	 * 重写 <code>Object</code> 类中的 <code>equals(Object obj)</code>
	 * 方法，支持瞬时态实体对象间基于业务主键的比较。
	 */
	@Override
	public boolean equals(Object obj) {
	    if (this == obj) {
			return true;
		}

		if (obj == null || !(this.getClass().isInstance(obj))) {
			return false;
		}

		StringBuilder sThis = new StringBuilder();
		StringBuilder sObj = new StringBuilder();
		
		AbstractEntity e = (AbstractEntity) obj;
		for (Method m : this.getClass().getMethods()) {
            if (m.isAnnotationPresent(Id.class)) {//FIXME:关于Id的获取方式
                try {
                    Object id1 = m.invoke(this);
                    Object id2 = m.invoke(e);
                    if(id1 == null && id2 == null) {
                        break;
                    }
                    if(id1 != null) {
                        return id1.equals(id2);
                    }
                    else if(id2 != null) {
                        return false;
                    }
                } catch (Exception ex) {
                    log.error("error in invoking getter method by reflection", ex);
                }
            }
        }
		
		//使用hashCode()进行比较
        for (Method m : this.getClass().getMethods()) {
            if (m.isAnnotationPresent(BusinessKey.class)) {//FIXME:关于Id的获取方式
                try {
                    Object key1 = m.invoke(this);
                    Object key2 = m.invoke(e);
                    if(key1 != null) {
                        sThis.append(key1.hashCode());
                    }
                    if(key2 != null) {
                        sObj.append(key2.hashCode());
                    }
                } catch (Exception ex) {
                    log.error("error in invoking getter method by reflection", ex);
                }
            }
        }
		
		
        if (!sThis.toString().equals(sObj.toString())){
            return false;
        }
        
        if(sThis.toString().length() != 0){
            return true;
        } else {
          return e.hashCode() == this.hashCode();
        }
	}

	/**
	 * 重写 <code>Object</code> 类中的 <code>hashCode()</code> 方法，计算基于业务主键的哈希散列值。
	 */
	@Override
	public int hashCode() {
		boolean hasBisKey = false;
		StringBuilder s = new StringBuilder();

		for (Method m : this.getClass().getMethods()) {
			if (m.isAnnotationPresent(BusinessKey.class)) {
				try {
					m.setAccessible(true);
					Object o = m.invoke(this); 
					if(o != null) s.append(o.hashCode());
					hasBisKey = true;
				} catch (Exception e) {
					log.error("error in invoking getter method by reflection", e);
				}
			}
		}
		if (hasBisKey) {
			return s.toString().hashCode();
		} else {
			return super.hashCode();
		}
	}

}
